(defvar catala-code-mode-hook nil)

(defun catala-set-syntax-table()
  (let ((st (syntax-table)))
    (modify-syntax-entry ?_ "w" st)
    (modify-syntax-entry ?% "_" st)

    (modify-syntax-entry ?- "." st)
    (modify-syntax-entry ?\; "." st)
    (modify-syntax-entry ?. "." st)
    (modify-syntax-entry ?, "." st)
    (modify-syntax-entry ?: "." st)

    (modify-syntax-entry ?$ "_" st)
    (modify-syntax-entry ?@ "_" st)
    (modify-syntax-entry ?€ "_" st)
    (modify-syntax-entry ?^ "_" st)

    (modify-syntax-entry ?| "$" st)
))

(define-generic-mode 'catala-mode-fr
  '("#")
  '("contexte" "entrée" "résultat" "interne"
    "champ d'application" "si et seulement si" "dépend de" "déclaration" "inclus" "liste de" "optionnel de" "maximum de" "minimum de" "nombre de" "contenu de" "contenu" "type" "optionnel" "structure" "énumération" "contexte" "entrée" "résultat" "interne" "règle" "sous condition" "condition" "donnée" "conséquence" "rempli" "égal à" "assertion" "définition" "état" "étiquette" "exception" "soit" "liste vide" "n'importe quel")
  '(("\\<\\(selon\\|sous\s+forme\\|mais\s+en\s+remplaçant\\|fixé\\|par\\|inférieur\\|supérieur\\|varie\\|avec\\|on\s+a\\|soit\\|dans\\|tel\s+que\\|existe\\|pour\\|tout\\|de\\|si\\|alors\\|sinon\\|initial\\|parmi\\|est\s+maximum\\|est\s+minimum\\|combine\\|transforme\s+chaque\\|en\\|initialement\\|impossible\\)\\>" . font-lock-builtin-face)
    ("\\<\\(vrai\\|faux\\)\\>" . font-lock-constant-face)
    ("\\<\\([0-9][0-9 ]*\\(,[0-9]*\\|\\)\\)\\>" . font-lock-constant-face)
    ("\\(->\\|+.\\|+@\\|+^\\|+€\\|+\\|-.\\|-@\\|-^\\|-€\\|-\\|*.\\|*@\\|*^\\|*€\\|*\\|/.\\|/@\\|/€\\|/\\|!\\|>.\\|>=.\\|<=.\\|<.\\|>@\\|>=@\\|<=@\\|<@\\|>€\\|>=€\\|<=€\\|<€\\|>^\\|>=^\\|<=^\\|<^\\|>\\|>=\\|<=\\|<\\|=\\)" . font-lock-keyword-face)
    ("\\<\\(\\|non\\|ou\s+bien\\|ou\\|et\\|€\\|%\\|an\\|mois\\|jour\\)\\>" . font-lock-keyword-face)
    ("\\<\\(entier\\|booléen\\|date\\|durée\\|argent\\|position_source\\|décimal\\|décret\\|loi\\|nombre\\|somme\\)\\>" . font-lock-type-face)
    ("\\<[a-zéèàâùîôêœç][a-zéèàâùîôêœçA-ZÉÈÀÂÙÎÔÊŒÇ0-9_']*\\>" . font-lock-variable-name-face)
    ("\\<[A-ZÉÈÀÂÙÎÔÊŒÇ][a-zéèàâùîôêœçA-ZÉÈÀÂÙÎÔÊŒÇ0-9_']*\\>" . font-lock-function-name-face)
    ("\\<\\(|[0-9]\\+-[0-9]\\+-[0-9]\\+|\\)\\>" . font-lock-constant-face)
  )
  '("\\.catala_fr_raw$")
  '(catala-set-syntax-table)
  "A basic generic major mode for catala files (fr version)"
)

(define-generic-mode 'catala-mode-en
  '("#")
  '("context" "input" "output" "internal"
    "scope" "depends on" "declaration" "includes" "list of" "optionnal of" "maximum of" "minimum of" "number of" "content of" "content" "type" "optional" "structure" "enumeration" "context" "input" "output" "internal" "rule" "under condition" "condition" "data" "consequence" "fulfilled" "equals" "assertion" "definition" "state" "label" "exception" "let" "list empty" "anything")
  '(("\\<\\(match\\|with\s+pattern\\|but\s+replace\\|fixed\\|by\\|down\\|up\\|varies\\|with\\|we\s+have\\|let\\|in\\|such\s+that\\|exists\\|for\\|all\\|of\\|if\\|then\\|else\\|initial\\|among\\|is\s+maximum\\|is\s+minimum\\|combine\\|map\s+each\\|to\\|initially\\|impossible\\)\\>" . font-lock-builtin-face)
    ("|[0-9]\\+-[0-9]\\+-[0-9]\\+|" . font-lock-constant-face)
    ("\\<\\(true\\|false\\)\\>" . font-lock-constant-face)
    ("\\<\\([0-9][0-9,]*\\(\\.[0-9]*\\|\\)\\)\\>" . font-lock-constant-face)
    ("\\(->\\|+.\\|+@\\|+^\\|+$\\|+\\|-.\\|-@\\|-^\\|-$\\|-\\|*.\\|*@\\|*^\\|*$\\|*\\|/.\\|/@\\|/$\\|/\\|!\\|>.\\|>=.\\|<=.\\|<.\\|>@\\|>=@\\|<=@\\|<@\\|>$\\|>=$\\|<=$\\|<$\\|>^\\|>=^\\|<=^\\|<^\\|>\\|>=\\|<=\\|<\\|=\\)" . font-lock-keyword-face)
    ("\\<\\(not\\|or\\|xor\\|and\\|\\$\\|%\\|year\\|month\\|day\\)\\>" . font-lock-keyword-face)
    ("\\<\\(integer\\|boolean\\|date\\|duration\\|money\\|code_location\\|decimal\\|number\\|sum\\)\\>" . font-lock-type-face)
    ("\\<[a-zéèàâùîôêœç][a-zéèàâùîôêœçA-ZÉÈÀÂÙÎÔÊŒÇ0-9_']*\\>" . font-lock-variable-name-face)
    ("\\<[A-ZÉÈÀÂÙÎÔÊŒÇ][a-zéèàâùîôêœçA-ZÉÈÀÂÙÎÔÊŒÇ0-9_']*\\>" . font-lock-function-name-face))
  '("\\.catala_en_raw$")
  '(catala-set-syntax-table)
  "A basic generic major mode for catala files (en version)"
)

(defun catala-code-mode()
  "Major mode for catala code (inside of ```catala blocks)"
  (interactive)
  (let* ((buf (window-buffer (minibuffer-selected-window)))
         (name (buffer-file-name buf)))
    (if (and name (string-match-p "\\.catala_fr" name))
        (catala-mode-fr)
      (catala-mode-en)))
  (run-mode-hooks 'catala-code-mode-hook))
;; needed due to <https://github.com/jrblevin/markdown-mode/pull/764>
(add-to-list 'auto-mode-alist '("\\.catala" . catala-code-mode))

(provide 'catala-code-mode)

;;;###autoload
(define-derived-mode catala-mode markdown-mode "Catala"
  "Catala major mode based on markdown-mode"
  (setq-local markdown-fontify-code-blocks-natively t)
  (setq-local markdown-code-lang-modes
   '(("catala-metadata" . catala-code-mode)
     ("catala" . catala-code-mode))))

(provide 'catala-mode)

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.catala_\\(fr\\|en\\|pl\\)" . catala-mode))

;; To use, add the following lines to ~/.emacs (without the leading ';') :
; (autoload 'catala-mode "catala-mode" "Catala major mode based on markdown-mode." t)
; (add-to-list 'auto-mode-alist '("\\.catala_\\(fr\\|en\\|pl\\)" . catala-mode))
;; You may additionally need the following if you don't already have any emacs setup in opam:
; (add-to-list 'load-path (concat (string-trim (shell-command-to-string "opam var prefix")) "/share/emacs/site-lisp"))
